import 'package:lib_base/logger/native_log_output.dart';
import 'package:logger/logger.dart' as logger_impl;
import 'package:logger/logger.dart';

enum Level {
  all(0),
  trace(1),
  debug(2),
  info(3),
  warning(4),
  error(5),
  fatal(6),
  off(7),
  ;

  final int value;

  const Level(this.value);

  factory Level.fromValue(int value) {
    return values.firstWhere((e) => e.value == value);
  }

  factory Level.fromLoggerLevel(logger_impl.Level level) {
    switch (level) {
      case logger_impl.Level.all:
        return Level.all;
      case logger_impl.Level.trace:
        return Level.trace;
      case logger_impl.Level.debug:
        return Level.debug;
      case logger_impl.Level.info:
        return Level.info;
      case logger_impl.Level.error:
        return Level.error;
      case logger_impl.Level.warning:
        return Level.warning;
      case logger_impl.Level.fatal:
        return Level.fatal;
      case logger_impl.Level.off:
        return Level.off;
      default:
        return Level.warning;
    }
  }

  logger_impl.Level get loggerLevel {
    switch (this) {
      case Level.all:
        return logger_impl.Level.all;
      case Level.trace:
        return logger_impl.Level.trace;
      case Level.debug:
        return logger_impl.Level.debug;
      case Level.info:
        return logger_impl.Level.info;
      case Level.warning:
        return logger_impl.Level.warning;
      case Level.error:
        return logger_impl.Level.error;
      case Level.fatal:
        return logger_impl.Level.fatal;
      case Level.off:
        return logger_impl.Level.off;
    }
  }
}

class Logger {
  static Level get level => Level.fromValue(logger_impl.Logger.level.value);
  static set level(Level value) => logger_impl.Logger.level = value.loggerLevel;

  static final Logger instance = Logger._internal();

  late final logger_impl.Logger _logger;

  void Function(Level, String)? nativeLogOutputCallback;

  Logger._internal();

  void initLogger() async {
    _logger = logger_impl.Logger(
      printer: logger_impl.SimplePrinter(),
      output: MultiOutput([
        NativeLogOutput(callback: (level, message) {
          nativeLogOutputCallback?.call(Level.fromLoggerLevel(level), message);
        }),
      ]),
    );
  }

  /// Log a message at level [Level.trace].
  void t(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.trace, message, time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message at level [Level.debug].
  void d(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.debug, message, time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message at level [Level.info].
  void i(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.info, message, time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message at level [Level.warning].
  void w(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.warning, message,
        time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message at level [Level.error].
  void e(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.error, message, time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message at level [Level.fatal].
  void f(
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    log(Level.fatal, message, time: time, error: error, stackTrace: stackTrace);
  }

  /// Log a message with [level].
  void log(
    Level level,
    dynamic message, {
    DateTime? time,
    Object? error,
    StackTrace? stackTrace,
  }) {
    _logger.log(
      level.loggerLevel,
      message,
      time: time,
      error: error,
      stackTrace: stackTrace,
    );
  }
}
